package org.ponly.uno4j.util;

import org.apache.pdfbox.cos.COSArray;
import org.apache.pdfbox.cos.COSObject;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.interactive.action.type.PDAction;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDDestination;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem;

import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * PDF 工具类
 *
 * @author vacoor
 */
public abstract class PdfUtils {

    @XmlRootElement
    public static class Bookmark {
        @XmlAttribute(name = "title")
        private String title;
        @XmlAttribute(name = "pageNumber")
        private Integer page;
        @XmlAnyElement
        private List<Bookmark> children;

        public String getTitle() {
            return title;
        }

        public Integer getPage() {
            return page;
        }

        public List<Bookmark> getChildren() {
            return children;
        }

        @Override
        public String toString() {
            StringBuilder buff = new StringBuilder();
            buff.append(title).append(" --- ").append(page);
            if (null != children) {
                for (Bookmark child : children) {
                    buff.append("\n\t").append(child);
                }
            }
            return buff.toString();
        }
    }

    public static int getNumberOfPages(File pdf) throws IOException {
        return load(pdf).getNumberOfPages();
    }

    public static PDDocument load(File pdf) throws IOException {
        return PDDocument.load(pdf);
    }

    /**
     * 获取 PDF 文档的封面图片
     *
     * @param doc PDF 文档
     * @return PDF 第一页的图片, 如果不存在返回null
     * @throws IOException
     */
    public static BufferedImage getCover(PDDocument doc) throws IOException {
        List<?> pages = doc.getDocumentCatalog().getAllPages();
        PDPage page = 0 < pages.size() ? (PDPage) pages.get(0) : null;
        return null != page ? page.convertToImage() : null;
    }

    /**
     * 获取给定 PDF 文档的所有书签
     *
     * @param doc PDF 文档
     * @return PDF 中包含的所有书签
     * @throws IOException
     */
    public static List<Bookmark> getBookmarks(final PDDocument doc) throws IOException {
        PDDocumentOutline bookmarks = doc.getDocumentCatalog().getDocumentOutline();
        PDOutlineItem bookmark = null != bookmarks ? bookmarks.getFirstChild() : null;
        return doParseBookmarks(bookmark, doc);
    }

    /**
     * 从 PDOutlineItem 中解析书签
     *
     * @param bookmark PDF 第一个书签
     * @param doc      PDF 文档对象
     * @return 该文档中的书签
     * @throws IOException
     */
    private static List<Bookmark> doParseBookmarks(PDOutlineItem bookmark, final PDDocument doc) throws IOException {
        List<Bookmark> marks = new ArrayList<Bookmark>();
        List<?> pages = null;

        while (null != bookmark) {
            final String title = bookmark.getTitle();
            final PDAction action = bookmark.getAction();
            final PDDestination dest = bookmark.getDestination();

            int page = -1;
            if (null != action) {
                // From BM-Thread
                COSObject targetPageRef = (COSObject) ((COSArray) bookmark.getAction().getCOSDictionary().getDictionaryObject("D")).get(0);
                String objStr = String.valueOf(targetPageRef.getObjectNumber().intValue());
                String genStr = String.valueOf(targetPageRef.getGenerationNumber().intValue());
                page = doc.getPageMap().get(objStr + "," + genStr);
            } else if (dest != null) {
                // From PDP-Thread
                if (null == pages) {
                    pages = new ArrayList<Object>();
                    doc.getDocumentCatalog().getPages().getAllKids(pages);
                }

                PDPage pdPage = bookmark.findDestinationPage(doc);
                page = pages.indexOf(pdPage) + 1;
            }

            Bookmark mark = new Bookmark();
            mark.title = title;
            mark.page = page;
            mark.children = doParseBookmarks(bookmark.getFirstChild(), doc);

            marks.add(mark);
            bookmark = bookmark.getNextSibling();
        }
        return marks;
    }

    /* ***************************************************
     *    com.itextpdf:itextpdf:5.5.0 读取 PDF Bookmarks
     *           因为不能转换到 image 所以放弃
     * ***************************************************/

    /*-
    private static List<Bookmark> getBookmarks(PdfReader reader) {
        // PdfReader pdfReader = new PdfReader(new FileInputStream(pdf));
        return doParseBookmarks(SimpleBookmark.getBookmark(reader));
    }

    @SuppressWarnings("unchecked")
    private static List<Bookmark> doParseBookmarks(final List<HashMap<String, Object>> bookmarks) {
        List<Bookmark> marks = new ArrayList<Bookmark>(bookmarks.size());
        for (Map<String, Object> bookmark : bookmarks) {
            if ("GoTo".equals(bookmark.get("Action"))) {
                final String title = (String) bookmark.get("Title");
                final String pageStr = (String) bookmark.get("Page");
                final List<HashMap<String, Object>> kids = (List<HashMap<String, Object>>) bookmark.get("Kids");
                final String[] loc = pageStr.split(" ", 5);

                int page = -1;
                if (0 < loc.length) {
                    try {
                        page = Integer.parseInt(loc[0], 10);
                    } catch (NumberFormatException ignore) {
                        // ignore
                    }
                }

                Bookmark mark = new Bookmark();
                mark.title = title;
                mark.page = page;

                if (null != kids) {
                    mark.children = doParseBookmarks(kids);
                }

                marks.add(mark);
            }
        }
        return marks;
    }
    */

    private PdfUtils() {
    }

    /*
    public static void main(String[] args) throws IOException {
        // file:///X:/besture/03.Doc/08.项目文档/Java数据库命名规范20150826.pdf
        File pdf = new File("D:\\a.pdf");
        List<Bookmark> bookmarks = null;

        PDDocument doc = PDDocument.load(pdf);
        String title = doc.getDocumentInformation().getTitle();
        int totalPages = doc.getNumberOfPages();

        try {
//            BufferedImage cover = getCover(doc);
            int numberOfPages = doc.getNumberOfPages();
            System.out.println(numberOfPages);
            bookmarks = getBookmarks(doc);
            for (Bookmark bookmark : bookmarks) {
                System.out.println(bookmark);
            }
        } finally {
            doc.close();
        }

        System.out.println("《" + title + "》共计" + totalPages + "页");
        Bookmark[] bks = toArray(bookmarks, Bookmark.class);
        String xml = JAXB2.marshalAny(bks, "bookmarks", "UTF-8");
        System.out.println(xml);
    }

    @SuppressWarnings("unchecked")
    private static <E> E[] toArray(List<?> list, Class<E> elementType) {
        E[] array = (E[]) Array.newInstance(elementType, list.size());
        return list.toArray(array);
    }
    */
}
